  void Panda::send_heartbeat() {
    usb_write(0xf3, 1, 0);
  }
  
  void Panda::can_send(capnp::List<cereal::CanData>::Reader can_data_list) {
    send.resize(4 * can_data_list.size());
  
    uint32_t msg_cnt = 0;
    for (int i = 0; i < can_data_list.size(); i++) {
      auto cmsg = can_data_list[i];
  
 
</s> remove   uint32_t msg_cnt = 0;
  for (int i = 0; i < can_data_list.size(); i++) {
    auto cmsg = can_data_list[i];
 </s> add void Panda::can_send(capnp::List<cereal::CanData>::Reader can_data_list) {
  send.resize(72 * can_data_list.size()); // TODO: need to include 1 byte for each usb 64bytes frame

  int msg_count = 0;
  while (msg_count < can_data_list.size()) {
    uint32_t pos = 0;
    while (pos < 256) {
      if (msg_count == can_data_list.size()) { break; }
      auto cmsg = can_data_list[msg_count];

      // check if the message is intended for this panda
      uint8_t bus = cmsg.getSrc();
      if (bus < bus_offset || bus >= (bus_offset + PANDA_BUS_CNT)) {
        msg_count++;
        continue;
      }
      auto can_data = cmsg.getDat();
      uint8_t data_len_code = len_to_dlc(can_data.size());
      assert(can_data.size() <= (hw_type == cereal::PandaState::PandaType::RED_PANDA) ? 64 : 8);
      assert(can_data.size() == dlc_to_len[data_len_code]);

      *(uint32_t*)&send[pos+1] = (cmsg.getAddress() << 3);
      if (cmsg.getAddress() >= 0x800) {
        *(uint32_t*)&send[pos+1] |= (1 << 2);
      }
      send[pos] = data_len_code << 4 | ((bus - bus_offset) << 1);
      memcpy(&send[pos+5], can_data.begin(), can_data.size()); </s> remove     // check if the message is intended for this panda
    uint8_t bus = cmsg.getSrc();
    if (bus < bus_offset || bus >= (bus_offset + PANDA_BUS_CNT)) {
      continue;
 </s> add       pos += CANPACKET_HEAD_SIZE + dlc_to_len[data_len_code];
      msg_count++; </s> remove     if (cmsg.getAddress() >= 0x800) { // extended
      send[msg_cnt*4] = (cmsg.getAddress() << 3) | 5;
    } else { // normal
      send[msg_cnt*4] = (cmsg.getAddress() << 21) | 1;
 </s> add     if (pos > 0) { // Helps not to spam with ZLP
      // insert counter
      uint8_t counter = 0;
      for (int i = 0; i < pos; i += 64) {
        send.insert(send.begin() + i, counter);
        counter++;
        pos++;
      }
      usb_bulk_write(3, (uint8_t*)send.data(), pos, 5); </s> remove     // add to vector
    out_vec.push_back(canData);
 </s> add   static uint8_t tail[72];
  uint8_t tail_size = 0;
  uint8_t counter = 0;
  for (int i = 0; i < recv; i += 64) {
    if (counter != data[i]) {
      LOGE("CAN: MALFORMED USB RECV PACKET");
      break;
    }
    counter++;
    uint8_t chunk_len = ((recv - i) > 64) ? 63 : (recv - i - 1); // as 1 is always reserved for counter
    uint8_t chunk[72];
    memcpy(chunk, tail, tail_size);
    memcpy(&chunk[tail_size], &data[i+1], chunk_len);
    chunk_len += tail_size;
    tail_size = 0;
    uint8_t pos = 0;
    while (pos < chunk_len) {
      uint8_t data_len = dlc_to_len[(chunk[pos] >> 4)];
      uint8_t pckt_len = CANPACKET_HEAD_SIZE + data_len;
      if (pckt_len <= (chunk_len - pos)) {
        can_frame canData;
        canData.busTime = 0;
        canData.address = (*(uint32_t*)&chunk[pos+1]) >> 3;
        canData.src = (chunk[pos] >> 1) & 0x7;

        bool rejected = chunk[pos+1] & 0x1;
        bool returned = (chunk[pos+1] >> 1) & 0x1;
        if (rejected) { canData.src += CANPACKET_REJECTED; }
        if (returned) { canData.src += CANPACKET_RETURNED; }
        canData.dat.assign((char*)&chunk[pos+5], data_len);

        pos += pckt_len;

        // add to vector
        out_vec.push_back(canData);
      } else {
        tail_size = (chunk_len - pos);
        memcpy(tail, &chunk[pos], tail_size);
        break;
      }
    } </s> remove   // Append to the end of the out_vec, such that we can pass it to multiple pandas
  // We already insert space for all the messages here for speed
  size_t num_msg = recv / 0x10;
  out_vec.reserve(out_vec.size() + num_msg);

  // Populate messages
  for (int i = 0; i < num_msg; i++) {
    can_frame canData;
    if (data[i*4] & 4) {
      // extended
      canData.address = data[i*4] >> 3;
      //printf("got extended: %x\n", data[i*4] >> 3);
    } else {
      // normal
      canData.address = data[i*4] >> 21;
    }
    canData.busTime = data[i*4+1] >> 16;
    int len = data[i*4+1] & 0xF;
    canData.dat.assign((char *)&data[i*4+2], len);
    canData.src = ((data[i*4+1] >> 4) & 0xff) + bus_offset;
 </s> add   out_vec.reserve(out_vec.size() + (recv / CANPACKET_HEAD_SIZE));
uint8_t Panda::len_to_dlc(uint8_t len) {
  if (len <= 8) {
    return len;
  }
  if (len <= 24) {
    return 8 + ((len - 8) / 4) + (len % 4) ? 1 : 0;
  } else {
    return 11 + (len / 16) + (len % 16) ? 1 : 0;
  }
}
  void Panda::send_heartbeat() {
    usb_write(0xf3, 1, 0);
  }
  
uint8_t Panda::len_to_dlc(uint8_t len) {
  if (len <= 8) {
    return len;
  }
  if (len <= 24) {
    return 8 + ((len - 8) / 4) + (len % 4) ? 1 : 0;
  } else {
    return 11 + (len / 16) + (len % 16) ? 1 : 0;
  }
}
uint8_t Panda::len_to_dlc(uint8_t len) {
  if (len <= 8) {
    return len;
  }
  if (len <= 24) {
    return 8 + ((len - 8) / 4) + (len % 4) ? 1 : 0;
  } else {
    return 11 + (len / 16) + (len % 16) ? 1 : 0;
  }
}
  
    uint32_t msg_cnt = 0;
    for (int i = 0; i < can_data_list.size(); i++) {
      auto cmsg = can_data_list[i];
  
keep keep keep keep replace replace keep keep keep keep keep
 New usb protocol for panda (#22752)

* first try

* move dlc_to_len

* panda switch to mailbox branch

* add bitstruct to pipfile

* bump panda and remove bitstruct

* few cleanups and fixes

* sanity check data.size == dlc_to_len

* check problem with test

* Revert "check problem with test"

This reverts commit b4855b7c12188d36d5d510e729344dab2cf21be9.

* clean

* revert Pipfile.lock

* that was an issue???

* fix crash

* ...

* include dlc_to_len from panda

* alph

* define macros for returned and rejected

* cleanup

* bump panda to current master 
selfdrive/boardd/panda.cc